from flask import Flask, url_for, render_template, redirect, request, flash
from markupsafe import escape
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
import secrets
from flask_login import (
    login_user, UserMixin, login_required, current_user,
    logout_user, LoginManager
)
import click

app = Flask(__name__)
app.config['SECRET_KEY'] = secrets.token_hex(16)

# 在 PythonAnywhere 或其它环境中改成对应的地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://songaoxiang:770804sax@songaoxiang.mysql.pythonanywhere-services.com:3306/songaoxiang$movies'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# 给 SQLAlchemy 设置连接池参数，防止空闲连接失效后出错
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
    'pool_pre_ping': True,   # 每次获取连接前先 ping，若连接已断开则重连
    'pool_recycle': 280      # 280 秒后回收连接，避免超时
}

db = SQLAlchemy(app)
migrate = Migrate(app, db)

# ------------------ 模型定义 ------------------
class Movie(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    year = db.Column(db.String(4), nullable=False)

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20))
    username = db.Column(db.String(20), unique=True, nullable=False)   # 用户名
    password = db.Column(db.String(128), nullable=False)               # 直接存明文密码

    def set_password(self, password):
        # 直接将明文密码保存进数据库，不进行加密/哈希
        self.password = password

    def validate_password(self, password):
        # 直接比较明文
        return self.password == password

# ------------------ 数据库初始化 ------------------
with app.app_context():
    db.create_all()

# ------------------ 视图函数 ------------------
@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        # 如果当前用户未登录，不允许新增
        if not current_user.is_authenticated:
            return redirect(url_for('index'))

        title = request.form.get('title')
        year = request.form.get('year')

        if not title or not year or len(year) > 4 or len(title) > 60:
            flash('Invalid input.')
            return redirect(url_for('index'))

        movie = Movie(title=title, year=year)
        db.session.add(movie)
        db.session.commit()
        flash('Item created.')
        return redirect(url_for('index'))

    movies = Movie.query.all()
    return render_template('index.html', movies=movies)

@app.route('/user/<name>')
def user_page(name):
    return f"User :{escape(name)}'s website"

@app.route('/test')
def test_url_for():
    print(url_for('index'))                      # /
    print(url_for('user_page', name='greyli'))   # /user/greyli
    print(url_for('test_url_for', num=2))        # /test?num=2
    return 'Test page'

# ------------------ 全局 context_processor ------------------
@app.context_processor
def inject_user():
    """
    在模板里注入 user 变量。
    一旦发生查询错误就回滚，避免 PendingRollbackError。
    """
    try:
        user = User.query.first()
    except Exception:
        db.session.rollback()
        user = None
    return dict(user=user)

# ------------------ 错误处理 ------------------
@app.errorhandler(404)
def page_not_found(e):
    db.session.rollback()
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
    db.session.rollback()
    return render_template('500.html'), 500

@app.errorhandler(400)
def bad_request(e):
    db.session.rollback()
    return render_template('400.html'), 400

# ------------------ 登录管理 ------------------
login_manager = LoginManager(app)
login_manager.login_view = 'login'

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        if not username or not password:
            flash('Invalid input.')
            return redirect(url_for('login'))

        # 调试输出：查看输入的用户名
        print(f"Looking for user with username: {username}")
        user = User.query.filter_by(username=username).first()
        if user:
            print(f"Found user: {user.username}")

        # 直接比较明文密码
        if user and user.validate_password(password):
            login_user(user)
            flash('Login success.')

            # 登录后可跳回来源页，否则默认到首页
            next_page = request.args.get('next')
            if not next_page or next_page.startswith('/logout'):
                next_page = url_for('index')
            return redirect(next_page)

        flash('Invalid username or password.')
        return redirect(url_for('login'))

    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('Goodbye.')
    return redirect(url_for('index'))

# ------------------ 一些增删改操作示例 ------------------
@app.route('/movie/edit/<int:movie_id>', methods=['GET', 'POST'])
@login_required
def edit(movie_id):
    movie = Movie.query.get_or_404(movie_id)

    if request.method == 'POST':
        title = request.form['title']
        year = request.form['year']

        if not title or not year or len(year) != 4 or len(title) > 60:
            flash('Invalid input.')
            return redirect(url_for('edit', movie_id=movie_id))

        movie.title = title
        movie.year = year
        db.session.commit()
        flash('Item updated.')
        return redirect(url_for('index'))

    return render_template('edit.html', movie=movie)

@app.route('/movie/delete/<int:movie_id>', methods=['POST'])
@login_required
def delete(movie_id):
    movie = Movie.query.get_or_404(movie_id)
    db.session.delete(movie)
    db.session.commit()
    flash('Item deleted.')
    return redirect(url_for('index'))

@app.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
    if request.method == 'POST':
        name = request.form['name']

        if not name or len(name) > 20:
            flash('Invalid input.')
            return redirect(url_for('settings'))

        current_user.name = name
        db.session.commit()
        flash('Settings updated.')
        return redirect(url_for('index'))

    return render_template('settings.html')

# ------------------ 自定义命令：生成测试数据/管理员账号 ------------------
@app.cli.command()
def forge():
    """Generate fake data."""
    db.create_all()
    name = 'Grey Li'
    movies_data = [
        {'title': 'My Neighbor Totoro', 'year': '1988'},
        {'title': 'Dead Poets Society', 'year': '1989'},
        {'title': 'A Perfect World', 'year': '1993'},
        {'title': 'Leon', 'year': '1994'},
        {'title': 'Mahjong', 'year': '1996'},
        {'title': 'Swallowtail Butterfly', 'year': '1996'},
        {'title': 'King of Comedy', 'year': '1999'},
        {'title': 'Devils on the Doorstep', 'year': '1999'},
        {'title': 'WALL-E', 'year': '2008'},
        {'title': 'The Pork of Music', 'year': '2012'},
    ]
    user = User(name=name, username='admin', password='123456')  # 默认直接设置明文密码
    db.session.add(user)
    for m in movies_data:
        movie = Movie(title=m['title'], year=m['year'])
        db.session.add(movie)
    db.session.commit()
    click.echo('Done.')

@app.cli.command()
@click.option('--username', prompt=True, help='The username used to login.')
@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True, help='The password used to login.')
def admin(username, password):
    """
    创建/更新用户: 不用哈希, 直接存明文密码.
    请记住, 这在生产环境下是不安全的!
    """
    db.create_all()
    user = User.query.first()
    if user is not None:
        click.echo('Updating user...')
        user.username = username
        user.set_password(password)  # 直接保存明文
    else:
        click.echo('Creating user...')
        user = User(username=username, name='Admin', password=password)
        db.session.add(user)

    db.session.commit()
    click.echo('Done.')
